#ifndef FILE_INTERFACE_H
#define FILE_INTERFACE_H
#include <stdint.h>
#include <string>
#include <exception>
#include <boost/dynamic_bitset.hpp>
#include <boost/thread/shared_mutex.hpp>
#include <boost/thread/mutex.hpp>

namespace egear{namespace file{ // egear::file

const int PieceSize = 1024*1024;

class CurlFileManager;
class IOException:public std::exception{
public:
    IOException(std::string _desc=""){
        if(_desc==""){
		desc="no further infomation";
	} else {
		desc=_desc;
	}
    };
    ~IOException() throw() {}
    const char *what() { return desc.c_str(); };
private:
    std::string desc;
};

class FileInterface{
    /*
     * handle a single file.
     * It is not thread-safe.
     */
public:
    /* 
     * constructor.
     * @param index: the index of this file in the FileManager.
     * @param folder: the folder where this file is in.
     * @param path: the path of this file in the folder. 
     *                It may contain subfolders.
     * @param size: the size of this file. If it is unkown, it should be -1.
     */
    FileInterface(uint32_t _index,const std::string &_folder,
		    const std::string &_path, int64_t _size=-1);

    virtual ~FileInterface();
    /*
     * get the index of this file in the FileManager.
     */
    virtual void open() throw(IOException);
    virtual void close();
    virtual bool isOpen();
    virtual uint32_t getIndex() const { return index; }
    /*
     * get the path of this file in the FileManager.
     */
    virtual std::string getPath() const { return path; }
    /*
     * get the path of this file on the disk.
     */
    virtual std::string getPathOnDisk() const { return pathOnDisk; }
    /*
     * set the on-disk path of the file.
     */
    virtual void setPathOnDisk(const std::string & p) { pathOnDisk = p; }
    /*
     * get the size of this file.
     */
    virtual int64_t getSize() const { return size; }
    /*
     * write to the file.
     * Seek to offset, write the data and return how many bytes
     * we had written.
     * @param  offset:   the offset we want to write to.
     * @data   data:     the data we want to write.
     */
    virtual int64_t write(int64_t offset, const std::string &data)
	    throw(IOException);
    virtual std::string read(int64_t offset, int len) throw(IOException);
protected:
    uint32_t index;
    std::string path;
    std::string pathOnDisk;
    int64_t size;
    FILE *file;
};

class SuspenseFile:public FileInterface{
    /*
     * file without checksum.
     * Its infomation is Suspense and will be 
     * determined when downloading.
     */
public:
    SuspenseFile(uint32_t _index,const std::string &_folder,
		    const std::string &_path, int64_t _size=-1);
    ~SuspenseFile();
    /*
     * set the size of the file.
     * It the size is already set and you want to set it to
     * another size, it return -1. Otherwise, it return 0.
     */
    virtual int resize(int64_t _size);
    virtual int getPieceAmount() { return pieceAmount; }
    virtual int isWorking(int pieceIndex) { return workingMask[pieceIndex]; }
    virtual int isDone(int pieceIndex) { return doneMask[pieceIndex]; }
    /*
     * Lock the piece.
     * If the is piece neither working nor done, it is set to working,
     * Or the function return -1, means failed to lock.
     */
    virtual int lockPiece(int pieceIndex);
    /*
     * Release the piece.
     */
    virtual void releasePiece(int pieceIndex);
    /*
     * Set piece to done.
     */
    virtual void setPieceDone(int pieceIndex);
protected:
    /*
     * our digest file recording the infomation of the file
     * and the status of the transmittion.
     */
    int pieceAmount;
    boost::dynamic_bitset<> workingMask;
    boost::dynamic_bitset<> doneMask;
    FILE *digestFile;
};

class SyncSuspenseFile:public SuspenseFile {
public:
    /*
     * Inherit SuspenseFile and add mutexes for synchronism
     */
    SyncSuspenseFile(uint32_t _index,const std::string &_folder,
		    const std::string &_path,
		    CurlFileManager *_fileManager, int64_t _size=-1);
    ~SyncSuspenseFile();
    int64_t write(int64_t offset, const std::string &data) throw(IOException);
    std::string read(int64_t offset, int len) throw(IOException);
   
protected:
    CurlFileManager *fileManager;    
    boost::mutex mutex;
};


}} // egear::file

#endif /* FILE_INTERFACE_H */
